﻿//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="DylanBox">
// Copyright (c) DylanBox Corporation. All rights reserved.
// </copyright>
// <summary>
//  creating the delegate in your code, actually created a class behind the scenes with a few methods called Invoke, BeginInvoke, and EndInvoke!!!!!

//     <Snippet from MSDN>
//         Delegates enable you to call a synchronous method in an asynchronous manner. 
//         
//         When you call a delegate synchronously, the Invoke method calls the target method directly on the current thread. 
//         
//         If the BeginInvoke method is called, the common language runtime (CLR) queues the request and returns immediately to the caller. 
//         The target method is called asynchronously on a thread from the thread pool. 
//         The original thread, which submitted the request, is free to continue executing in parallel with the target method. 

//         If a callback method has been specified in the call to the BeginInvoke method, the callback method is called when the target method ends. 
//         In the callback method, the EndInvoke method obtains the return value and any input/output or output-only parameters. 
//         If no callback method is specified when calling BeginInvoke, EndInvoke can be called from the thread that called BeginInvoke.
//     </Snippet from MSDN>

//  Result：we can see EndInvoke is a blocking call
//  
//      Before making the Async call... Thread ID = 1
//      After making the Async call... Thread ID = 1
//      Perform more work as the other thread works...
//      The slow method... executes...on thread 3
//      10...
//      9...
//      8...
//      7...
//      6...
//      5...
//      The async task is going on... 3
//      4...
//      3...
//      2...
//      Waiting for the async call to return now...
//      The async task is going on... 3
//      The async task is going on... 3
//      The async task is going on... 3
//      The async task is going on... 3
//      The call got executed on thread 3
//      The value returned was - I worked in my sleep for 30 seconds
// </summary>
// <reference>
//  http://www.dotnetscraps.com/dotnetscraps/post/Explaining-Delegates-in-C-Part-4-(Asynchronous-Callback-Way-1).aspx
// </reference>
// <author>Dylan</author>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace EventAndDelegateDemo
{
    //The delegate must have the same signature as the method. In this case,
    //we will make it same as TortoiseMethod
    public delegate string TortoiseCaller(int seconds, out int threadId);

    public class TortoiseClass
    {
        // The method to be executed asynchronously.
        public string TortoiseMethod(int seconds, out int threadId)
        {
            Console.WriteLine("The slow method... executes...on thread {0}", Thread.CurrentThread.ManagedThreadId);
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(seconds / 5 * 1000);
                Console.WriteLine("The async task is going on... {0}", Thread.CurrentThread.ManagedThreadId);
            }
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("I worked in my sleep for {0} seconds", seconds.ToString());
        }
    }

    //Now, that we are done with the declaration part, let's proceed to
    //consume the classes and see it in action
    //The algorithm would be very simple...
    //         1. Call delegate's BeginInvoke
    //         2. Do some work on the main thread
    //         3. Call the delegate's EndInvoke
    public class TortoiseConsumer
    {
        public static void Main()
        {
            //Instantiate a new TortoiseClass
            TortoiseClass tc = new TortoiseClass();
            //Let's create the delegate now
            TortoiseCaller caller = new TortoiseCaller(tc.TortoiseMethod);
            //The asynchronous method puts the thread id here
            int threadId;
            //Make the async call. Notice that this thread continues to run after making this call
            Console.WriteLine("Before making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId);
            IAsyncResult result = caller.BeginInvoke(30, out threadId, null, null);
            Console.WriteLine("After making the Async call... Thread ID = {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Perform more work as the other thread works...");
            for (int i = 10; i > 1; i--)
            {
                Thread.Sleep(1000);
                Console.WriteLine("{0}...", i);
            }
            Console.WriteLine("Waiting for the async call to return now...");
            //Notice that this call will be a blocking call
            string returnValue = caller.EndInvoke(out threadId, result);
            Console.WriteLine("The call got executed on thread {0}", threadId);
            Console.WriteLine("The value returned was - {0}", returnValue);

            Console.Read();
        }
    }
}
